﻿#region 참조 네임스페이스

using Skying.ComponentModel;
using System;
using System.ComponentModel;
using System.Net;
using System.Net.Sockets;

#endregion

namespace Skying.Net.Sockets
{
	/// <summary>
	/// <see cref="BaseSocket"/>을 기반으로 하여 UDP 소켓 리스너를 구현한다.
	/// </summary>
	/// <remarks>
	/// <para><see cref="BaseSocket"/>을 상속하는 이유는 이벤트 인터페이스의 공유와 공용 상수와 필드의 사용을 위함이다.</para>
	/// <para><see cref="BaseSocket"/>을 상속하는 것 뿐만 아니라, <see cref="UdpClient"/>와의 통신을 위한 매개체로 <see cref="BaseSocket"/>을 사용한다.</para>
	/// </remarks>
	public class UdpListener : BaseSocket
	{
		#region 프로퍼티

		/// <summary>
		/// 리스너가 수신 대기 상태인지 아닌지를 가져온다.
		/// </summary>
		public bool IsListening
		{
			get;
			private set;
		}

		/// <summary>
		/// TCP 리스너가 클라이언트을 수신할 수 있는 상태가 됐을 때 실행된다.
		/// </summary>
		public Action<AsyncCompletedEventArgs> ListeningStarted
		{
			get;
			set;
		}

		/// <summary>
		/// TCP 리스너가 클라이언트로부터 수신을 중지하고 포트 바인딩을 해제했을 때 실행된다.
		/// </summary>
		public Action<AsyncCompletedEventArgs> ListeningStopped
		{
			get;
			set;
		}

		#endregion

		#region 생성자/소멸자

		public UdpListener(int port/*, TransferType transferType = Sockets.TransferType.Both*/, bool useInternalHeader = true)
			: this(port, 1024/*, transferType*/, useInternalHeader)
		{

		}

		public UdpListener(int port, int bufferSize/*, TransferType transferType = Sockets.TransferType.Both*/, bool useInternalHeader = true)
			: base(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp/*, transferType*/, useInternalHeader)
		{
			try
			{
				SynchronizationContextRegister.Verify(SynchronizationContextProperties.NonReentrantPost | SynchronizationContextProperties.NonReentrantSend | SynchronizationContextProperties.Sequential | SynchronizationContextProperties.Synchronized);

				base.Port = port;

				base._pieceBufferSize = bufferSize;
			}
			catch (Exception error)
			{
				throw error;
			}
		}

		#endregion

		#region 소켓 컨트롤

		/// <summary>
		/// 소켓을 수신 상태로 둔다.
		/// </summary>
		/// <returns>소켓이 수신 상태가 된 경우 true, 그렇지 않으면 false가 반환된다.</returns>
		public bool Listen()
		{
			try
			{
				base.Address = IPAddress.Any;

				// 로컬 끝점을 정의한다.
				IPEndPoint localEndPoint = new IPEndPoint(base.Address, base.Port);

				// 소켓을 로컬 끝점과 연결한다.
				base._socket.Bind(localEndPoint);

				Synchronization.InvokeAndCallback(() => { this.IsListening = true; }, ListeningStarted, null);

				IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);

				base.ReceiveAsynchrousFrom(remoteEndPoint);

				return true;
			}
			catch (Exception error)
			{
				throw error;
			}
		}

		/// <summary>
		/// 모든 클라이언트로 데이터를 브로드캐스팅한다.
		/// </summary>
		/// <param name="data">브로드캐스팅할 데이터를 설정한다.</param>
		public void Close()
		{
			try
			{
				if (!this.IsListening) return;

				base._socket.Close();

				Synchronization.InvokeAndCallback(() => { this.IsListening = false; }, ListeningStopped, null);

				this.ListeningStarted = null;
				this.ListeningStopped = null;
			}
			catch (Exception error)
			{
				throw error;
			}
		}

		#endregion
	}
}